/*********************************************************************
*
* Software License Agreement (BSD License)
*
*  Copyright (c) 2010, Heriot-Watt University, UK.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Heriot-Watt University nor the names of
*     its contributors may be used to endorse or promote products
*     derived from this software without specific prior written
*     permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
*  Author: Joel Cartwright
*
*********************************************************************/

#ifndef MATCHMAKER_SERVER_H_
#define MATCHMAKER_SERVER_H_

#include <vector>
#include <map>

#include <ros/ros.h>

#include <matchmaker_msgs/Advert.h>
#include <matchmaker_msgs/FindServices.h>
#include <matchmaker_msgs/GetServiceStatus.h>
#include <matchmaker_msgs/ClientUpdate.h>
#include <matchmaker_msgs/ServerUpdate.h>

#include <matchmaker/version.h>
#include <matchmaker/service_status.h>
#include <matchmaker/service_id.h>


namespace matchmaker {

/// Map of service ID to advert message
typedef std::map<ServiceID, matchmaker_msgs::AdvertConstPtr> M_ServiceToAdvertMsg;

/// Map of string to client update message
typedef std::map<std::string, matchmaker_msgs::ClientUpdateConstPtr> M_StringToClientUpdateMsg;

/// Map of string to ROS publisher
typedef std::map<std::string, ros::Publisher> M_StringToPublisher;

/// Map of string to ROS timer
typedef std::map<std::string, ros::Timer> M_StringToTimer;

/// Map of string to set of strings
typedef std::map<std::string, boost::shared_ptr<std::set<std::string> > > M_StringToStringSet;

/**
 * @brief The central SOA service registry
 *
 * This acts a central service registry, which nodes use to advertise
 * and discover services via the Client interface.
 *
 * There should only be one Server running under a given ROS master.
 */
class Server
{
public:
  Server();

  void run();

  static std::string getVersionString();

protected:
  static const double ADVERT_EXPIRY_TIME = 4;
  static const double MONITOR_EXPIRY_TIME = 4;
  static const double EXPIRY_CHECK_PERIOD = 1;
  static const double SERVER_UDPATE_MSG_SEND_PERIOD = 1;
  static const double CLIENT_UPDATE_MSG_TIMEOUT = 4;
  static const double SCHEDULED_SEND_TIMEOUT = 0.000001;

  void waitForClock();

  void checkExpiredCB(const ros::TimerEvent& event);

  void checkExpiredAdverts();

  void checkExpiredClients();

  void handleAdvertMsg(const ros::MessageEvent<matchmaker_msgs::Advert const>& event);

  void handleClientUpdateMsg(const ros::MessageEvent<matchmaker_msgs::ClientUpdate const>& event);

  void handleClientMonitoringUpdate(const std::string& clientName,
      matchmaker_msgs::ClientUpdateConstPtr msg,
      matchmaker_msgs::ClientUpdateConstPtr lastMsg);

  void handleClientQueryingUpdate(const std::string& clientName,
      matchmaker_msgs::ClientUpdateConstPtr msg,
      matchmaker_msgs::ClientUpdateConstPtr lastMsg);

  void removeClientDetails(const std::string & name);

  void addMonitoredService(const std::string & clientName, const std::string & serviceId);

  void removeMonitoredService(const std::string & clientName, const std::string & serviceId);

  void addQueriedService(const std::string & cilentName, const std::string & serviceName);

  void removeQueriedService(const std::string & clientName, const std::string & serviceName);

  void sendAllServerUpdateMsgs();

  void sendServerUpdateMsgsForServiceId(const std::string & serviceId);

  void sendServerUpdateMsgsForServiceName(const std::string & serviceName);

  void scheduleSendServerUpdateMsg(const std::string & clientName);

  void sendServerUpdateMsg(const std::string & name);

  void fillServerUpdateForMonitors(matchmaker_msgs::ClientUpdateConstPtr& clientMsg,
      matchmaker_msgs::ServerUpdate& updateMsg);

  void fillServerUpdateForQueries(matchmaker_msgs::ClientUpdateConstPtr& clientMsg,
      matchmaker_msgs::ServerUpdate& updateMsg);

  bool handleFindServices(matchmaker_msgs::FindServices::Request& request,
      matchmaker_msgs::FindServices::Response& response);

  bool handleGetServiceStatus(matchmaker_msgs::GetServiceStatus::Request& request,
      matchmaker_msgs::GetServiceStatus::Response& response);

  static std::string statusToText(int16_t status);

  ros::NodeHandlePtr nh_;

  M_ServiceToAdvertMsg advertMsgMap_;

  M_StringToClientUpdateMsg clientUpdateMsgMap_;

  M_StringToPublisher serverUpdatePubMap_;

  M_StringToTimer serverUpdateOneshotTimerMap_;

  M_StringToStringSet monitoredIdClientMap_;

  M_StringToStringSet queriedNameClientMap_;


  ros::Subscriber advertSub_;

  ros::Timer expiryCheckTimer_;

  ros::Subscriber clientUpdateMsgSub_;

  ros::Timer serverUpdateSendTimer_;

  ros::ServiceServer findServicesServer_;

  ros::ServiceServer getServiceStatusServer_;
};

}

#endif /* MATCHMAKER_SERVER_H_ */
